#include "mp3_reader.h"
#include <stdio.h>
#include <limits.h>

KMp3Decoder::KMp3Decoder()
{
	mSamplesInOutput = 0;
	mStatus = 0;
	mCurrentFrameIndex = 0;
	mOutputPtr = (unsigned char *) mOutputBuffer, 
	mGuardPtr = NULL;
	mStreamPtr = NULL;
	mpStreamData = NULL;
	mStreamSize = 0;
	
	mIsPlaying = false;
}

KMp3Decoder::~KMp3Decoder()
{
	if (mStreamPtr) {
		mad_synth_finish(&mSynth);
		mad_frame_finish(&mFrame);
		mad_stream_finish(&mStream);
		delete mStreamPtr;
	}
}

/****************************************************************************
* Converts a sample from libmad's fixed point number format to a signed	*
* short (16 bits).															*
****************************************************************************/
signed short KMp3Decoder::MadFixedToSshort(mad_fixed_t Fixed)
{
    // Clipping 
    if (Fixed >= MAD_F_ONE)
	return (SHRT_MAX);
    if (Fixed <= -MAD_F_ONE)
	return (-SHRT_MAX);

    // Conversion
    Fixed = Fixed >> (MAD_F_FRACBITS - 15);
    return ((signed short) Fixed);
}

unsigned int KMp3Decoder::MixWithStreamBuffer(short* buffer, unsigned int numSamples)
{
	signed short *_buf = (short *)buffer;
    unsigned int samplesOut = 0;
	unsigned int numSubSample = numSamples * 2;

    if (mIsPlaying) {	
		
		if (mSamplesInOutput > 0) {
			
			if ((unsigned int)mSamplesInOutput > numSubSample) {
				for (unsigned int count = 0; count < numSubSample; count++)
					*(_buf + count) += mOutputBuffer[count];
					
				samplesOut = numSubSample;
				mSamplesInOutput -= numSubSample;
			} 
			else {
				for (int count = 0; count < mSamplesInOutput; count++)
					*(_buf + count) += mOutputBuffer[count];
					
				samplesOut = mSamplesInOutput;
				mSamplesInOutput = 0;
			}
		}

		while (samplesOut < numSubSample) {
			if (mStream.buffer == NULL || mStream.error == MAD_ERROR_BUFLEN) {

			mStream.error = MAD_ERROR_NONE;
			}

			if (mad_frame_decode(&mFrame, &mStream)) {
				if (MAD_RECOVERABLE(mStream.error)) {
					if (mStream.error != MAD_ERROR_LOSTSYNC || mStream.this_frame != mGuardPtr) {
						// Serious error
					}
					return -1;	
				} else if (mStream.error == MAD_ERROR_BUFLEN) {
					mIsEOS = 1;
					return -1;
				} else {
					// unrecoverable frame level error
					mStatus = 1;
					break;
				}
			}

			mCurrentFrameIndex++;
			mad_timer_add(&mTimer, mFrame.header.duration);

			// Decode a frame
 			mad_synth_frame(&mSynth, &mFrame);

			for (int i = 0; i < mSynth.pcm.length; i++) {
				
				if (samplesOut < numSubSample) {
					
					// Left channel 
					short leftSample = MadFixedToSshort(mSynth.pcm.samples[0][i]);
					_buf[samplesOut++] += leftSample;

					// Right channel. If the decoded stream is monophonic then
					// the right output channel is the same as the left one.
					if (MAD_NCHANNELS(&mFrame.header) == 2)
						_buf[samplesOut++] += MadFixedToSshort(mSynth.pcm.samples[1][i]);
					else
						_buf[samplesOut++] = leftSample;

				} else {
					short leftSample = MadFixedToSshort(mSynth.pcm.samples[0][i]);
					mOutputBuffer[mSamplesInOutput++] = leftSample;

					if (MAD_NCHANNELS(&mFrame.header) == 2) 
						mOutputBuffer[mSamplesInOutput++] = MadFixedToSshort(mSynth.pcm.samples[1][i]);
					else
						mOutputBuffer[mSamplesInOutput++] = leftSample;
				}

			}
		}
    } 
	else {			//  Not Playing , so clear buffer
    }
	
	return samplesOut / 2;
}

bool KMp3Decoder::OpenFile(const char* fileName)
{
	mStreamPtr = new KFileReader(fileName);
	if (!mStreamPtr->IsFileOpen()) {
		delete mStreamPtr;
		mStreamPtr = NULL;
		return false;
	}

	mpStreamData = mStreamPtr->CopyIntoMemory();
	mStreamSize = mStreamPtr->GetFileSize();
	Reset();
	return true;

}

void KMp3Decoder::Reset()
{
	mIsEOS = 0;
	mIsPlaying = true;
	mCurrentFrameIndex = 0;
	mad_timer_reset(&mTimer);

	mCurrentFrameIndex = 0;
	// First the structures used by libmad must be initialized. 
	mad_stream_init(&mStream);
	mad_frame_init(&mFrame);
	mad_synth_init(&mSynth);
	// Pipe the new buffer content to libmad's stream decoder facility.
	mad_stream_buffer(&mStream, (unsigned char*)mpStreamData, mStreamSize);
}

bool KMp3Decoder::IsEOF() const
{
	return (mIsEOS != 0 ? true : false);
}

